﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

using SpectrEngine.Framework;
using SpectrEngine.Framework.Input;
using CellWarzGame.Collections;

using CellWarzGame.Screens;
using SpectrEngine.Framework.Collision;

namespace CellWarzGame.Actors
{
    public class Cell
        : CollidableGameNode
    {
        public enum Side
        {
            red=0,
            green=1,
            blue=2,
            yellow=3,
            orange=4,
            violet=5,
            virus=6//gray
        }
        public enum Controler
        {
            player, 
            AI,
            Tail
        }
        public enum CellType
        {
            Monk,
            Punk,
            Skunk,
            Virus,
            Tail

        }
        public enum ActionState
        {
            Attack,
            Chase,
            Flee,
            Wander,
            Death
        }

        const int CellAnimationFrameTime = 33;

        protected bool markedForDeath;

        TimeSpan attackTimer;

        public Side side;
        public static Color[] SideColor = { Color.Red, Color.Green, Color.Blue, Color.Yellow, Color.Orange, Color.Violet, Color.Gray };
        public float maxhp;
        public float hp;
        public float maxSpeed;
        public float angle;
        public Cell enemy;
        public float enemyChangeCooldown;
        public float sensorDistance;
        public float attackPower;
        public int kills;
        public Controler controler;
        Random rand;
        public CellType cellType;
        protected ActionState actionState;
        public ActionState Action
        {
            get
            {
                return actionState;
            }
            set
            {
                SetActionState(value);
            }
        }
        public float toClose;
        public float toFar;

        public float PunkCooldown;
        public float SkunkCooldown;
        public float MonkCooldown;
        public int score;

        public float invunlerableTime = 0;

        protected List<Texture2D> spriteSheets;
        protected List<Rectangle[]> spriteSheetRects;
        protected int currentSprite;
        protected int currentSheet;
        protected int cellAnimationTime;
        protected Rectangle moveArea;

        public Cell(Game game, Side _side, Controler _controler, Vector2 position, CellType _cellType)
            : base(game, position, 0.0f, Vector2.One)
        {
            attackTimer = new TimeSpan(0, 0, 0);
            markedForDeath = false;
            rand = new Random((int)(position.X + position.Y));
            angle = (float)rand.NextDouble() * (float)Math.PI * 2.0f;
            side = _side;
            controler = _controler;
            cellType = _cellType;
            cellAnimationTime = 0;
            InitCollision();
            InitSpriteSheets();
            hp = 100;
            maxSpeed = 80.0f;
            moveArea = game.GraphicsDevice.Viewport.TitleSafeArea;

            game.Services.GetService<IPlayScreenService>().Register(this);
            game.Services.GetService<ICellListService>().Register(this);
        }

        public override void Dispose()
        {
            base.Dispose();
        }


        protected virtual void InitCollision()
        {
            hitZones = new BoundingCircle[1];
            hitZones[0] = new BoundingCircle(Vector2.Zero, 100.0f);

            attackZones = new BoundingCircle[1];
            attackZones[0] = new BoundingCircle(Vector2.Zero, 100.0f);

            bounds = new BoundingRect(75.0f, 75.0f);
        }

        protected virtual void InitSpriteSheets()
        {
            spriteSheets = new List<Texture2D>();
            spriteSheetRects = new List<Rectangle[]>();
            switch (cellType)
            {
                case CellType.Monk:
                    spriteSheets.Add(Game.Content.Load<Texture2D>("Images\\MunkIdleSheet"));
                    spriteSheetRects.Add(GetSpriteSheetRects(spriteSheets[spriteSheets.Count - 1], 200, 200));
                    spriteSheets.Add(Game.Content.Load<Texture2D>("Images\\MunkAttackSheet"));
                    spriteSheetRects.Add(GetSpriteSheetRects(spriteSheets[spriteSheets.Count - 1], 200, 200));
                    spriteSheets.Add(Game.Content.Load<Texture2D>("Images\\MunkDeathSheet"));
                    spriteSheetRects.Add(GetSpriteSheetRects(spriteSheets[spriteSheets.Count - 1], 200, 200));
                    break;
                case CellType.Punk:
                    spriteSheets.Add(Game.Content.Load<Texture2D>("Images\\PunkIdleSheet"));
                    spriteSheetRects.Add(GetSpriteSheetRects(spriteSheets[spriteSheets.Count - 1], 200, 200));
                    spriteSheets.Add(Game.Content.Load<Texture2D>("Images\\PunkAttackSheet"));
                    spriteSheetRects.Add(GetSpriteSheetRects(spriteSheets[spriteSheets.Count - 1], 200, 200));
                    spriteSheets.Add(Game.Content.Load<Texture2D>("Images\\PunkDeathSheet"));
                    spriteSheetRects.Add(GetSpriteSheetRects(spriteSheets[spriteSheets.Count - 1], 200, 200));
                    break;
                case CellType.Skunk:
                    spriteSheets.Add(Game.Content.Load<Texture2D>("Images\\SkunkIdleSheet"));
                    spriteSheetRects.Add(GetSpriteSheetRects(spriteSheets[spriteSheets.Count - 1], 200, 200));
                    spriteSheets.Add(Game.Content.Load<Texture2D>("Images\\SkunkAttackSheet"));
                    spriteSheetRects.Add(GetSpriteSheetRects(spriteSheets[spriteSheets.Count - 1], 200, 200));
                    spriteSheets.Add(Game.Content.Load<Texture2D>("Images\\SkunkDeathSheet"));
                    spriteSheetRects.Add(GetSpriteSheetRects(spriteSheets[spriteSheets.Count - 1], 200, 200));
                    break;
                case CellType.Tail:
                    spriteSheets.Add(Game.Content.Load<Texture2D>("Images\\SegmentCycleSheet"));
                    spriteSheetRects.Add(GetSpriteSheetRects(spriteSheets[spriteSheets.Count - 1], 150, 150));
                    break;
            }
            currentSprite = 0;
            currentSheet = 0;
        }

        protected static Rectangle[] GetSpriteSheetRects( Texture2D texture, int spriteWidth, int spriteHeight )
        {
            List<Rectangle> rects = new List<Rectangle>();

            for (int y = 0; y < texture.Height; y += spriteHeight)
            {
                for (int x = 0; x < texture.Width; x += spriteWidth)
                {
                    rects.Add(new Rectangle(x, y, spriteWidth, spriteHeight));
                }
            }

            return rects.ToArray();
        }


        public override void CollideWith(CollidableGameNode other)
        {
            base.CollideWith(other);

            if (other is Cell)
            {
                Cell enemy = (Cell)other;

                if (this.side == enemy.side)
                {
                    return;
                }

                if (enemy.cellType != CellType.Tail)
                {
                    if (enemy.Action == ActionState.Attack)
                    {

                        if (hp > 0)
                        {
                            actionState = ActionState.Flee;

                            hp--;
                        }
                        else
                        {
                            Action = ActionState.Death;
                            //currentSprite = 0;
                        }
                    }

                    if (actionState == ActionState.Attack)
                    {
                        if (enemy.hp <= 0)
                        {

                            enemy.Action = ActionState.Death;
                        }
                        else
                        {
                            actionState = ActionState.Flee;

                            enemy.hp--;
                        }
                    }
                }
            }

        }

        public override void Update(GameTime gameTime)
        {
            if (cellType != CellType.Tail)
            {
                enemy = GetClosestEnemy();
            }

            if (this.actionState == ActionState.Attack)
            {
                attackTimer -= gameTime.ElapsedGameTime;

                if (attackTimer.TotalMilliseconds < 0)
                {
                    attackTimer = TimeSpan.FromMilliseconds(0);
                }
            }
            else if (this.actionState == ActionState.Death)
            {
                attackTimer = TimeSpan.FromMilliseconds(0);
            }

            invunlerableTime -= (float)gameTime.ElapsedGameTime.TotalSeconds;
            
            UpdateMovement(gameTime);

            int lastFrame  = currentSprite % spriteSheetRects[currentSheet].Count() + 1;

            if ((lastFrame == spriteSheetRects[currentSheet].Count()) )
            {
                if ((this.cellType == CellType.Tail) ||
                    ((actionState == ActionState.Death) && (currentSheet == 2)))
                {
                    this.Dispose();
                }

            }

            UpdateCellAnimation(gameTime);

        }

        private Cell GetClosestEnemy()
        {
            IList<DrawableGameNode> cellList = this.Game.Services.GetService<ICellListService>().GetNearest(this.Position);
            if (cellList.Count > 0)
            {
                foreach (Cell cell in cellList)
                {
                    if ((cell != this) && (this.side != cell.side))
                    {
                        
                        return cell;
                    }
                }
            }
            return null;
        }


        protected virtual void UpdateMovement( GameTime gameTime )
        {
            Vector2 v = new Vector2(0, 0);
            if (controler == Controler.AI)
            {
                v = AI.AI.Decide(this);

                Vector2 spot = this.Position + v;

                if (moveArea.Contains((int)spot.X, (int)spot.Y))
                    Translate(v * (float)gameTime.ElapsedGameTime.TotalSeconds * maxSpeed);
                else
                {
                    Vector2 ofset = new Vector2(0, 0);
                    if (spot.X > moveArea.Width)
                        ofset.X -= moveArea.Width;
                    if (spot.Y > moveArea.Height)
                        ofset.Y -= moveArea.Height;
                    if (spot.X < 0)
                        ofset.X += moveArea.Width;
                    if (spot.Y < 0)
                        ofset.Y += moveArea.Height;

                    Translate(v * (float)gameTime.ElapsedGameTime.TotalSeconds * maxSpeed + ofset);
                }
            }
            else if (controler == Controler.Tail)
            {
                AI.AI.TailSegmentAct(this);
                this.side = this.enemy.side;
            }
        }

        protected void UpdateCellAnimation(GameTime gameTime)
        {
            if (cellAnimationTime > CellAnimationFrameTime)
            {
                currentSprite++;
                currentSprite = currentSprite % spriteSheetRects[currentSheet].Count();
                cellAnimationTime = 0;
            }
            cellAnimationTime += gameTime.ElapsedGameTime.Milliseconds;
        }

        public override void Draw(GameTime gameTime, Microsoft.Xna.Framework.Graphics.SpriteBatch spriteBatch)
        {
            Texture2D texture = spriteSheets[currentSheet];
            Rectangle rect = spriteSheetRects[currentSheet][currentSprite];

            if (rand.NextDouble() > 0.5 || (actionState != ActionState.Flee))
                spriteBatch.Draw(texture, position, rect, SideColor[(int)side], rotation + (61 * MathHelper.TwoPi / 360.0f),
                    new Vector2(rect.Width / 2, rect.Height / 2), size,
                    SpriteEffects.None, 0);

            /*Color c = Color.Black;
            if (actionState == ActionState.Attack) c = Color.Red;
            if (actionState == ActionState.Chase) c = Color.Yellow;
            if (actionState == ActionState.Wander) c = Color.Green;
            if (actionState == ActionState.Flee) c = Color.Cyan;
            if (actionState == ActionState.Death) c = Color.White;

            spriteBatch.Draw(texture, position, rect, c, rotation + (61 * MathHelper.TwoPi / 360.0f),
                new Vector2(rect.Width / 2, rect.Height / 2), size/2,
                SpriteEffects.None, 0);*/
        }

        protected virtual void SetActionState(ActionState actionStateParam)
        {
            if (this.actionState != actionStateParam)
            {
                // don't change the state if attacking, this ensures that the attack animation finishes
                if ((this.actionState == ActionState.Attack) && (attackTimer.TotalMilliseconds > 0))
                {
                    return;
                }

                this.actionState = actionStateParam;
                if (cellType != CellType.Virus && cellType != CellType.Tail)
                {
                    switch (actionState)
                    {
                        case ActionState.Attack:
                            currentSheet = 1;
                            attackTimer = TimeSpan.FromMilliseconds(500);
                            break;
                        case ActionState.Death:
                            currentSheet = 2;
                            break;
                        default:
                            currentSheet = 0;
                            break;
                    }
                    currentSprite = 0;
                }
            }
        }
    }
}
